﻿Imports NSRegExp = System.Text.RegularExpressions

Public Enum ENumberFormat
    Dec
    Oct
    BinBE
    BinLE
    HexBE
    HexLE
End Enum

Public Class CTokenParcerEscapeSequence
    Inherits CTokenParcerStdBase

    Private Class CDicWrapTranslator
        Implements NSCllG.IDictionary(Of String, String)

        Private mdicMain As NSCllG.IDictionary(Of String, String)
        Private mdicKeyLen As NSCllG.SortedList(Of Int32, Int32)


        Public Sub New(ByVal objSeed As CDicWrapTranslator)
            Dim dicMain = New NSCllG.Dictionary(Of String, String)
            Dim dicKeyLen = New NSCllG.SortedList(Of Int32, Int32)

            For Each objKVMainIt In objSeed.mdicMain
                dicMain.Add(objKVMainIt.Key, objKVMainIt.Value)
            Next

            For Each objKVKeyLenIt In objSeed.mdicKeyLen
                dicKeyLen.Add(objKVKeyLenIt.Key, objKVKeyLenIt.Value)
            Next
        End Sub


        Public Sub New(ByVal cllKVMain_i As NSCllG.ICollection(Of NSCllG.KeyValuePair(Of String, String)))
            MainDicSet(cllKVMain_i)
        End Sub

        Public Sub New(ByVal dicMain_i As NSCllG.IDictionary(Of String, String))
            MainDicSet(dicMain_i)
        End Sub

        Private Sub MainDicSet(cllTranslation_i As NSCllG.ICollection(Of NSCllG.KeyValuePair(Of String, String)))
            If cllTranslation_i Is Nothing OrElse cllTranslation_i.Count = 0 Then
                Throw New ArgumentException("CTokenParcerEscapeSequenceDic must have translations.", "cllTranslation_i")
            End If

            Dim dicMain = New NSCllG.Dictionary(Of String, String)
            Dim dicKeyLen = New NSCllG.SortedList(Of Int32, Int32)

            For Each objKVIt In cllTranslation_i
                Dim numLen = objKVIt.Key.Length

                If dicKeyLen.ContainsKey(numLen) Then
                    dicKeyLen.Item(numLen) = dicKeyLen.Item(numLen) + 1
                Else
                    dicKeyLen.Add(numLen, 1)
                End If

                dicMain.Add(objKVIt.Key, objKVIt.Value)
            Next objKVIt

            mdicMain = dicMain
            mdicKeyLen = dicKeyLen
        End Sub

        Private Sub MainDicSet(dicTranslation_i As NSCllG.IDictionary(Of String, String))
            If dicTranslation_i Is Nothing OrElse dicTranslation_i.Count = 0 Then
                Throw New ArgumentException("CTokenParcerEscapeSequenceDic must have translations.", "dicTranslation_i")
            End If

            Dim dicMain = New NSCllG.Dictionary(Of String, String)
            Dim dicKeyLen = New NSCllG.SortedList(Of Int32, Int32)

            For Each objKVIt In dicTranslation_i
                Dim numLen = objKVIt.Key.Length

                If dicKeyLen.ContainsKey(numLen) Then
                    dicKeyLen.Item(numLen) = dicKeyLen.Item(numLen) + 1
                Else
                    dicKeyLen.Add(numLen, 1)
                End If

                dicMain.Add(objKVIt.Key, objKVIt.Value)
            Next objKVIt

            mdicMain = dicMain
            mdicKeyLen = dicKeyLen
        End Sub

        Public Property MainDic As NSCllG.IDictionary(Of String, String)
            Get
                Return mdicMain
            End Get
            Set(dic_i As NSCllG.IDictionary(Of String, String))
                MainDicSet(dic_i)
            End Set
        End Property

        Public ReadOnly Property KeyLenCll As NSCllG.IList(Of Int32)
            Get
                Return mdicKeyLen.Keys
            End Get
        End Property

        Private Sub Impl_Add(key As String, value As String) Implements System.Collections.Generic.IDictionary(Of String, String).Add
            Dim numLen = key.Length

            Dim dic = mdicMain
            Dim dicKeyLen = mdicKeyLen

            mdicMain = Nothing
            mdicKeyLen = Nothing

            If dicKeyLen.ContainsKey(numLen) Then
                dicKeyLen.Item(numLen) = dicKeyLen.Item(numLen) + 1
            Else
                dicKeyLen.Add(numLen, 1)
            End If

            dic.Add(key, value)

            mdicMain = dic
            mdicKeyLen = dicKeyLen
        End Sub

        Private Sub Impl_Add(item As System.Collections.Generic.KeyValuePair(Of String, String)) Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of String, String)).Add
            Dim numLen = item.Key.Length

            Dim dic = mdicMain
            Dim dicKeyLen = mdicKeyLen

            mdicMain = Nothing
            mdicKeyLen = Nothing

            If dicKeyLen.ContainsKey(numLen) Then
                dicKeyLen.Item(numLen) = mdicKeyLen.Item(numLen) + 1
            Else
                dicKeyLen.Add(numLen, 1)
            End If

            dic.Add(item)

            mdicMain = dic
            mdicKeyLen = dicKeyLen
        End Sub

        Private Sub Impl_Clear() Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of String, String)).Clear
            Throw New InvalidOperationException("CTokenParcerEscapeSequenceDic must have translations.")
        End Sub

        Private Function Impl_Contains(item As System.Collections.Generic.KeyValuePair(Of String, String)) As Boolean Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of String, String)).Contains
            Return mdicMain.Contains(item)
        End Function

        Private Sub Impl_CopyTo(array() As System.Collections.Generic.KeyValuePair(Of String, String), arrayIndex As Integer) Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of String, String)).CopyTo
            mdicMain.CopyTo(array, arrayIndex)
        End Sub

        Private ReadOnly Property Impl_Count As Integer Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of String, String)).Count
            Get
                Return mdicMain.Count
            End Get
        End Property

        Private ReadOnly Property Impl_IsReadOnly As Boolean Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of String, String)).IsReadOnly
            Get
                Return mdicMain.IsReadOnly
            End Get
        End Property

        Private Function Impl_ContainsKey(key As String) As Boolean Implements System.Collections.Generic.IDictionary(Of String, String).ContainsKey
            Return mdicMain.ContainsKey(key)
        End Function

        Default Property Impl_Item(key As String) As String Implements System.Collections.Generic.IDictionary(Of String, String).Item
            Get
                Return mdicMain.Item(key)
            End Get
            Set(value As String)
                mdicMain.Item(key) = value
            End Set
        End Property

        Private ReadOnly Property Impl_Keys As System.Collections.Generic.ICollection(Of String) Implements System.Collections.Generic.IDictionary(Of String, String).Keys
            Get
                Return mdicMain.Keys
            End Get
        End Property

        Private Function Impl_Remove(item As System.Collections.Generic.KeyValuePair(Of String, String)) As Boolean Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of String, String)).Remove
            If mdicMain.Count = 1 AndAlso mdicMain.Contains(item) Then
                Throw New InvalidOperationException("CTokenParcerEscapeSequenceDic must have translations.")
            End If

            If mdicMain.Remove(item) Then
                Dim numLen = item.Key.Length

                If mdicKeyLen.Item(numLen) = 1 Then
                    mdicKeyLen.Remove(numLen)
                Else
                    mdicKeyLen.Item(numLen) = mdicKeyLen.Item(numLen) - 1
                End If

                Return True
            Else
                Return False
            End If
        End Function

        Private Function Impl_Remove(key As String) As Boolean Implements System.Collections.Generic.IDictionary(Of String, String).Remove
            If mdicMain.Count = 1 AndAlso mdicMain.ContainsKey(key) Then
                Throw New InvalidOperationException("CTokenParcerEscapeSequenceDic must have translations.")
            End If

            If mdicMain.Remove(key) Then
                Dim numLen = key.Length

                If mdicKeyLen.Item(numLen) = 1 Then
                    mdicKeyLen.Remove(numLen)
                Else
                    mdicKeyLen.Item(numLen) = mdicKeyLen.Item(numLen) - 1
                End If

                Return True
            Else
                Return False
            End If
        End Function

        Private Function Impl_TryGetValue(key As String, ByRef value As String) As Boolean Implements System.Collections.Generic.IDictionary(Of String, String).TryGetValue
            Return mdicMain.TryGetValue(key, value)
        End Function

        Private ReadOnly Property Impl_Values As System.Collections.Generic.ICollection(Of String) Implements System.Collections.Generic.IDictionary(Of String, String).Values
            Get
                Return mdicMain.Values
            End Get
        End Property

        Private Function Impl_GetEnumerator() As System.Collections.Generic.IEnumerator(Of System.Collections.Generic.KeyValuePair(Of String, String)) Implements System.Collections.Generic.IEnumerable(Of System.Collections.Generic.KeyValuePair(Of String, String)).GetEnumerator
            Return CType(mdicMain, NSCllG.IEnumerable(Of System.Collections.Generic.KeyValuePair(Of String, String))).GetEnumerator()
        End Function

        Private Function Impl_GetEnumeratorL() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
            Return CType(mdicMain, NSCll.IEnumerable).GetEnumerator()
        End Function
    End Class

    Private mdicTranslation As NSCllG.IDictionary(Of String, String)
    Private mobjDicWrapTranslation As CDicWrapTranslator
    Protected mstrMarkOpen As String
    Protected mstrKeyFounded As String

    Protected Sub TranslatorDicSet(ByVal dic_i As NSCllG.IDictionary(Of String, String))
        mobjDicWrapTranslation.MainDic = dic_i
        mdicTranslation = dic_i
    End Sub

    Private Sub InitMe(ByVal strMarkOpen_i As String, ByVal dicTranslation_i As NSCllG.IDictionary(Of String, String))
        If String.IsNullOrEmpty(strMarkOpen_i) Then
            Throw New ArgumentException("Parameter can not be empty or null.", "strEscapeMarkOpen_i")
        End If

        mobjDicWrapTranslation = New CTokenParcerEscapeSequence.CDicWrapTranslator(dicTranslation_i)
        mstrMarkOpen = strMarkOpen_i
        mstrKeyFounded = Nothing
    End Sub

    Private Sub InitMe(ByVal strMarkOpen_i As String, ByVal cllTranslation_i As NSCllG.ICollection(Of NSCllG.KeyValuePair(Of String, String)))
        If String.IsNullOrEmpty(strMarkOpen_i) Then
            Throw New ArgumentException("Parameter can not be empty or null.", "strEscapeMarkOpen_i")
        End If

        mobjDicWrapTranslation = New CTokenParcerEscapeSequence.CDicWrapTranslator(cllTranslation_i)
        mstrMarkOpen = strMarkOpen_i
        mstrKeyFounded = Nothing
    End Sub


    Sub New(objSeed As CTokenParcerEscapeSequence)
        MyBase.New(objSeed)
        With objSeed
            mobjDicWrapTranslation = New CDicWrapTranslator(.mdicTranslation)
            mdicTranslation = mobjDicWrapTranslation.MainDic
            mstrMarkOpen = .mstrMarkOpen
            mstrKeyFounded = .mstrKeyFounded
        End With
    End Sub


    Public Sub New(numRank_i As Int32, enmType_i As ETokenParcerStdType, ByVal strMarkOpen_i As String, ByVal cllTranslation_i As NSCllG.ICollection(Of NSCllG.KeyValuePair(Of String, String)))
        MyBase.New(numRank_i, enmType_i)
        InitMe(strMarkOpen_i, cllTranslation_i)
    End Sub

    Public Sub New(numRank_i As Int32, objTokenFactory_i As ITokenFactory, ByVal strMarkOpen_i As String, ByVal cllTranslation_i As NSCllG.ICollection(Of NSCllG.KeyValuePair(Of String, String)))
        MyBase.New(numRank_i, objTokenFactory_i)
        InitMe(strMarkOpen_i, cllTranslation_i)
    End Sub

    Public Sub New(numRank_i As Int32, ByVal strMarkOpen_i As String, ByVal cllTranslation_i As NSCllG.ICollection(Of NSCllG.KeyValuePair(Of String, String)))
        MyBase.New(numRank_i)
        InitMe(strMarkOpen_i, cllTranslation_i)
    End Sub

    Public Sub New(numRank_i As Int32, enmType_i As ETokenParcerStdType, ByVal strMarkOpen_i As String, ByVal dicTranslation_i As NSCllG.IDictionary(Of String, String))
        MyBase.New(numRank_i, enmType_i)
        InitMe(strMarkOpen_i, dicTranslation_i)
    End Sub

    Public Sub New(numRank_i As Int32, objTokenFactory_i As ITokenFactory, ByVal strMarkOpen_i As String, ByVal dicTranslation_i As NSCllG.IDictionary(Of String, String))
        MyBase.New(numRank_i, objTokenFactory_i)
        InitMe(strMarkOpen_i, dicTranslation_i)
    End Sub

    Public Sub New(numRank_i As Int32, ByVal strMarkOpen_i As String, ByVal dicTranslation_i As NSCllG.IDictionary(Of String, String))
        MyBase.New(numRank_i)
        InitMe(strMarkOpen_i, dicTranslation_i)
    End Sub

    Public Property TranslationDic As NSCllG.IDictionary(Of String, String)
        Get
            Return mobjDicWrapTranslation
        End Get
        Set(dic_i As NSCllG.IDictionary(Of String, String))
            TranslatorDicSet(dic_i)
        End Set
    End Property

    Public Property EscapeMarkOpen As String
        Get
            Return mstrMarkOpen
        End Get
        Set(str_i As String)
            If String.IsNullOrEmpty(str_i) Then
                Throw New ArgumentException("Parameter can not be empty or null.", "Key")
            End If

            mstrMarkOpen = str_i
        End Set
    End Property

    Protected Overrides Sub Impl_BegParce(strTextRaw_i As String, ByRef numPosCurr_io As Integer, ByRef numRank_o As Integer)
        If MStringUtil.StringEqualsSafe(strTextRaw_i, numPosCurr_io, mstrMarkOpen) Then
            Dim numPosBeg = numPosCurr_io + mstrMarkOpen.Length
            Dim cllKeyLen = mobjDicWrapTranslation.KeyLenCll

            mstrKeyFounded = Nothing

            For k As Int32 = cllKeyLen.Count - 1 To 0 Step -1
                Dim numLenIt = cllKeyLen(k)
                mstrKeyFounded = strTextRaw_i.Substring(numPosBeg, numLenIt)

                If mobjDicWrapTranslation.MainDic.ContainsKey(mstrKeyFounded) Then
                    numRank_o = mnumRank
                    Exit For
                End If
            Next
        End If
    End Sub

    Protected Overrides Sub Impl_EndParce(strTextRaw_i As String, ByRef numPosCurr_io As Integer, ByRef objToken_o As CTokenBase)
        Dim numPosBeg = numPosCurr_io
        Dim numLength = mstrMarkOpen.Length + mstrKeyFounded.Length

        numPosCurr_io += numLength
        TokenCreateSafe(strTextRaw_i, numPosBeg, CUInt(numLength), mobjDicWrapTranslation.MainDic.Item(mstrKeyFounded), objToken_o)
    End Sub

End Class

Public Class CTokenParcerEscapeSequenceFixLenCharCode
    Inherits CTokenParcerStdBase

    Protected mstrMarkOpen As String
    Protected mblnIsBE As Boolean
    Protected menmCharCodeFormat As ENumberFormat
    Protected mnumCharCodeLen As Int32
    Protected mnumConvertionBase As Int32
    Protected mnumCharPerByte As Byte
    Protected mobjREValue As NSRegExp.Regex
    Protected mstrCharCodeFounded As String
    Protected mobjEncoding As Text.Encoding

    Private Sub InitMe(ByVal strMarkOpen_i As String, ByVal numCharCodeLen_i As Int32, ByVal enmCharCodeFormat_i As ENumberFormat, ByVal objEncoding_i As Text.Encoding)
        If String.IsNullOrEmpty(strMarkOpen_i) Then
            Throw New ArgumentNullException("Parameter can not be empty or null.", "strKey_i")
        End If

        mstrMarkOpen = strMarkOpen_i

        CharCodeKindSet(numCharCodeLen_i, enmCharCodeFormat_i, objEncoding_i)
    End Sub

    Sub New(objSeed As CTokenParcerEscapeSequenceFixLenCharCode)
        MyBase.New(objSeed)
        With objSeed
            mstrMarkOpen = .mstrMarkOpen
            mblnIsBE = .mblnIsBE
            menmCharCodeFormat = .menmCharCodeFormat
            mnumCharCodeLen = .mnumCharCodeLen
            mnumConvertionBase = .mnumConvertionBase
            mnumCharPerByte = .mnumCharPerByte
            mobjREValue = .mobjREValue
            mstrCharCodeFounded = Nothing
            mobjEncoding = .mobjEncoding
        End With
    End Sub


    Protected Sub New(numRank_i As Int32)
        MyBase.New(numRank_i)
    End Sub

    Protected Sub New(numRank_i As Int32, objTokenFactory_i As ITokenFactory)
        MyBase.New(numRank_i, objTokenFactory_i)
    End Sub

    Protected Sub New(numRank_i As Int32, enmType_i As ETokenParcerStdType)
        MyBase.New(numRank_i, enmType_i)
    End Sub


    Public Sub New(numRank_i As Int32, ByVal strMarkOpen_i As String, ByVal numCharCodeLen_i As Int32, ByVal enmCharCodeFormat_i As ENumberFormat)
        MyBase.New(numRank_i)
        InitMe(strMarkOpen_i, numCharCodeLen_i, enmCharCodeFormat_i, Text.Encoding.UTF8)
    End Sub

    Public Sub New(numRank_i As Int32, ByVal strMarkOpen_i As String, ByVal numCharCodeLen_i As Int32, ByVal enmCharCodeFormat_i As ENumberFormat, ByVal objEncoding_i As Text.Encoding)
        MyBase.New(numRank_i)
        InitMe(strMarkOpen_i, numCharCodeLen_i, enmCharCodeFormat_i, objEncoding_i)
    End Sub


    Public Sub New(numRank_i As Int32, objTokenFactory_i As ITokenFactory, ByVal strMarkOpen_i As String, ByVal numCharCodeLen_i As Int32, ByVal enmCharCodeFormat_i As ENumberFormat)
        MyBase.New(numRank_i, objTokenFactory_i)
        InitMe(strMarkOpen_i, numCharCodeLen_i, enmCharCodeFormat_i, Text.Encoding.UTF8)
    End Sub

    Public Sub New(numRank_i As Int32, objTokenFactory_i As ITokenFactory, ByVal strMarkOpen_i As String, ByVal numCharCodeLen_i As Int32, ByVal enmCharCodeFormat_i As ENumberFormat, ByVal objEncoding_i As Text.Encoding)
        MyBase.New(numRank_i, objTokenFactory_i)
        InitMe(strMarkOpen_i, numCharCodeLen_i, enmCharCodeFormat_i, objEncoding_i)
    End Sub


    Public Sub New(numRank_i As Int32, enmType_i As ETokenParcerStdType, ByVal strMarkOpen_i As String, ByVal numCharCodeLen_i As Int32, ByVal enmCharCodeFormat_i As ENumberFormat)
        MyBase.New(numRank_i, enmType_i)
        InitMe(strMarkOpen_i, numCharCodeLen_i, enmCharCodeFormat_i, Text.Encoding.UTF8)
    End Sub

    Public Sub New(numRank_i As Int32, enmType_i As ETokenParcerStdType, ByVal strMarkOpen_i As String, ByVal numCharCodeLen_i As Int32, ByVal enmCharCodeFormat_i As ENumberFormat, ByVal objEncoding_i As Text.Encoding)
        MyBase.New(numRank_i, enmType_i)
        InitMe(strMarkOpen_i, numCharCodeLen_i, enmCharCodeFormat_i, objEncoding_i)
    End Sub


    Public Property EscapeMarkOpen As String
        Get
            Return mstrMarkOpen
        End Get
        Set(str_i As String)
            If String.IsNullOrEmpty(str_i) Then
                Throw New ArgumentException("Parameter can not be empty or null.", "EscapeMarkClose")
            End If

            mstrMarkOpen = str_i
        End Set
    End Property

    Public Overridable ReadOnly Property EscapeSequenceLen As Int32
        Get
            Return mstrMarkOpen.Length + mnumCharCodeLen
        End Get
    End Property

    Public Overridable ReadOnly Property CharCodeLen As Int32
        Get
            Return mnumCharCodeLen
        End Get
    End Property

    Public Overridable ReadOnly Property CharCodeFormat As ENumberFormat
        Get
            Return menmCharCodeFormat
        End Get
    End Property

    Public Overridable ReadOnly Property CharCodeEncoding As Text.Encoding
        Get
            Return mobjEncoding
        End Get
    End Property

    Public Overridable Sub CharCodeKindSet(ByVal numCharCodeLen_i As Int32, ByVal enmCharCodeFormat_i As ENumberFormat, ByVal objEncoding_i As Text.Encoding)
        If numCharCodeLen_i <= 0 Then
            Throw New ArgumentOutOfRangeException("numCharCodeLen_i", "Value must be non zero positive.")
        End If

        If objEncoding_i Is Nothing Then
            Throw New ArgumentNullException("objEncoding_i")
        End If

        mnumCharCodeLen = numCharCodeLen_i
        menmCharCodeFormat = enmCharCodeFormat_i
        mobjEncoding = objEncoding_i

        Dim numCharCodeLenMax As Int32

        Select Case menmCharCodeFormat
            Case ENumberFormat.Dec
                mobjREValue = New NSRegExp.Regex("^\d+", NSRegExp.RegexOptions.Compiled Or NSRegExp.RegexOptions.Singleline Or NSRegExp.RegexOptions.CultureInvariant)
                mblnIsBE = True
                mnumConvertionBase = 10
                numCharCodeLenMax = 10
            Case ENumberFormat.BinBE
                mobjREValue = New NSRegExp.Regex("^[01]+", NSRegExp.RegexOptions.Compiled Or NSRegExp.RegexOptions.Singleline Or NSRegExp.RegexOptions.CultureInvariant)
                mblnIsBE = True
                mnumConvertionBase = 2
                mnumCharPerByte = 8
                numCharCodeLenMax = mnumCharPerByte * 4
            Case ENumberFormat.BinLE
                mobjREValue = New NSRegExp.Regex("^[01]+", NSRegExp.RegexOptions.Compiled Or NSRegExp.RegexOptions.Singleline Or NSRegExp.RegexOptions.CultureInvariant)
                mblnIsBE = False
                mnumConvertionBase = 2
                mnumCharPerByte = 8
                numCharCodeLenMax = mnumCharPerByte * 4
            Case ENumberFormat.Oct
                mobjREValue = New NSRegExp.Regex("^[0-7]+", NSRegExp.RegexOptions.Compiled Or NSRegExp.RegexOptions.Singleline Or NSRegExp.RegexOptions.CultureInvariant)
                mblnIsBE = True
                mnumConvertionBase = 8
                numCharCodeLenMax = 11
            Case ENumberFormat.HexBE
                mobjREValue = New NSRegExp.Regex("^[0-9A-Fa-f]+", NSRegExp.RegexOptions.Compiled Or NSRegExp.RegexOptions.Singleline Or NSRegExp.RegexOptions.CultureInvariant)
                mblnIsBE = True
                mnumConvertionBase = 16
                mnumCharPerByte = 2
                numCharCodeLenMax = mnumCharPerByte * 4
            Case ENumberFormat.HexLE
                mobjREValue = New NSRegExp.Regex("^[0-9A-Fa-f]+", NSRegExp.RegexOptions.Compiled Or NSRegExp.RegexOptions.Singleline Or NSRegExp.RegexOptions.CultureInvariant)
                mblnIsBE = False
                mnumConvertionBase = 16
                mnumCharPerByte = 2
                numCharCodeLenMax = mnumCharPerByte * 4
            Case Else
                Throw New ArgumentOutOfRangeException("enmCharCodeFormat_i")
        End Select

        If (Not mblnIsBE And numCharCodeLen_i Mod mnumCharPerByte <> 0) Then
            Throw New ArgumentOutOfRangeException("numCharCodeLen_i")
        End If

        If numCharCodeLen_i > numCharCodeLenMax Then
            Throw New ArgumentOutOfRangeException(String.Format("numCharCodeLen_i", "Max value is {0}", numCharCodeLenMax))
        End If
    End Sub

    Protected Overrides Sub Impl_BegParce(strTextRaw_i As String, ByRef numPosCurr_io As Integer, ByRef numRank_o As Integer)
        mstrCharCodeFounded = Nothing

        If strTextRaw_i.Length >= numPosCurr_io + mnumCharCodeLen + mstrMarkOpen.Length _
        AndAlso strTextRaw_i.Substring(numPosCurr_io, mstrMarkOpen.Length) = mstrMarkOpen Then
            Dim objREM = mobjREValue.Match(strTextRaw_i.Substring(numPosCurr_io + mstrMarkOpen.Length, mnumCharCodeLen))

            If objREM.Length = mnumCharCodeLen Then
                mstrCharCodeFounded = objREM.Value
                numRank_o = mnumRank
            End If
        End If
    End Sub

    Protected Overrides Sub Impl_EndParce(strTextRaw_i As String, ByRef numPosCurr_io As Integer, ByRef objToken_o As CTokenBase)
        Dim numPosBeg = numPosCurr_io
        Dim strTextParced As String = Nothing

        If mblnIsBE Then
            strTextParced = mobjEncoding.GetChars(BitConverter.GetBytes(Convert.ToInt32(mstrCharCodeFounded, mnumConvertionBase)))(0)
        Else
            Dim numBytLen = mnumCharCodeLen \ mnumCharPerByte
            Dim cllByteCode = BitConverter.GetBytes(Convert.ToInt32(mstrCharCodeFounded, mnumConvertionBase))
            Array.Reverse(cllByteCode, 0, numBytLen)
            strTextParced = mobjEncoding.GetChars(cllByteCode, 0, numBytLen)(0)
        End If

        Dim numLength = mstrMarkOpen.Length + mstrCharCodeFounded.Length

        numPosCurr_io += numLength
        TokenCreateSafe(strTextRaw_i, numPosBeg, CUInt(numLength), strTextParced, objToken_o)
    End Sub

End Class

Public Class CTokenParcerEscapeSequenceVarLenCharCode
    Inherits CTokenParcerEscapeSequenceFixLenCharCode

    Protected mstrMarkClose As String
    Protected mnumEscapeSequenceLen As Int32

    Private Sub InitMe(ByVal strMarkOpen_i As String, ByVal strMarkClose_i As String, ByVal numCharCodeLen_i As Int32, ByVal enmCharCodeFormat_i As ENumberFormat, ByVal objEncoding_i As Text.Encoding)
        If String.IsNullOrEmpty(strMarkOpen_i) Then
            Throw New ArgumentNullException("Parameter can not be empty or null.", "strKey_i")
        End If

        mstrMarkOpen = strMarkOpen_i

        If String.IsNullOrEmpty(strMarkClose_i) Then
            Throw New ArgumentException("Parameter can not be empty or null.", "strKeyEnd_i")
        End If

        mstrMarkClose = strMarkClose_i

        CharCodeKindSet(numCharCodeLen_i, enmCharCodeFormat_i, objEncoding_i)
    End Sub


    Sub New(objSeed As CTokenParcerEscapeSequenceVarLenCharCode)
        MyBase.New(objSeed)
        With objSeed
            mstrMarkClose = .mstrMarkClose
            mnumEscapeSequenceLen = .mnumEscapeSequenceLen
        End With
    End Sub


    Public Sub New(numRank_i As Int32, ByVal strMarkOpen_i As String, ByVal strMarkClose_i As String, ByVal numCharCodeLen_i As Int32, ByVal enmCharCodeFormat_i As ENumberFormat)
        MyBase.New(numRank_i)
        InitMe(strMarkOpen_i, strMarkClose_i, numCharCodeLen_i, enmCharCodeFormat_i, Text.Encoding.UTF8)
    End Sub

    Public Sub New(numRank_i As Int32, ByVal strMarkOpen_i As String, ByVal strMarkClose_i As String, ByVal numCharCodeLen_i As Int32, ByVal enmCharCodeFormat_i As ENumberFormat, ByVal objEncoding_i As Text.Encoding)
        MyBase.New(numRank_i)
        InitMe(strMarkOpen_i, strMarkClose_i, numCharCodeLen_i, enmCharCodeFormat_i, objEncoding_i)
    End Sub


    Public Sub New(numRank_i As Int32, objTokenFactory_i As ITokenFactory, ByVal strMarkOpen_i As String, ByVal strMarkClose_i As String, ByVal numCharCodeLen_i As Int32, ByVal enmCharCodeFormat_i As ENumberFormat)
        MyBase.New(numRank_i, objTokenFactory_i)
        InitMe(strMarkOpen_i, strMarkClose_i, numCharCodeLen_i, enmCharCodeFormat_i, Text.Encoding.UTF8)
    End Sub

    Public Sub New(numRank_i As Int32, objTokenFactory_i As ITokenFactory, ByVal strMarkOpen_i As String, ByVal strMarkClose_i As String, ByVal numCharCodeLen_i As Int32, ByVal enmCharCodeFormat_i As ENumberFormat, ByVal objEncoding_i As Text.Encoding)
        MyBase.New(numRank_i, objTokenFactory_i)
        InitMe(strMarkOpen_i, strMarkClose_i, numCharCodeLen_i, enmCharCodeFormat_i, objEncoding_i)
    End Sub


    Public Sub New(numRank_i As Int32, enmType_i As ETokenParcerStdType, ByVal strMarkOpen_i As String, ByVal strMarkClose_i As String, ByVal numCharCodeLen_i As Int32, ByVal enmCharCodeFormat_i As ENumberFormat)
        MyBase.New(numRank_i, enmType_i)
        InitMe(strMarkOpen_i, strMarkClose_i, numCharCodeLen_i, enmCharCodeFormat_i, Text.Encoding.UTF8)
    End Sub

    Public Sub New(numRank_i As Int32, enmType_i As ETokenParcerStdType, ByVal strMarkOpen_i As String, ByVal strMarkClose_i As String, ByVal numCharCodeLen_i As Int32, ByVal enmCharCodeFormat_i As ENumberFormat, ByVal objEncoding_i As Text.Encoding)
        MyBase.New(numRank_i, enmType_i)
        InitMe(strMarkOpen_i, strMarkClose_i, numCharCodeLen_i, enmCharCodeFormat_i, objEncoding_i)
    End Sub

    Public Property EscapeMarkClose As String
        Get
            Return mstrMarkClose
        End Get
        Set(str_i As String)
            If String.IsNullOrEmpty(str_i) Then
                Throw New ArgumentException("Parameter can not be empty or null.", "EscapeMarkClose")
            End If

            mstrMarkClose = str_i
        End Set
    End Property

    Public Overrides Sub CharCodeKindSet(ByVal numCharCodeLen_i As Int32, ByVal enmCharCodeFormat_i As ENumberFormat, ByVal objEncoding_i As Text.Encoding)
        If numCharCodeLen_i <= 0 Then
            Throw New ArgumentOutOfRangeException("numCharCodeLen_i", "Value must be non zero positive.")
        End If

        If objEncoding_i Is Nothing Then
            Throw New ArgumentNullException("objEncoding_i")
        End If

        mobjEncoding = objEncoding_i
        menmCharCodeFormat = enmCharCodeFormat_i
        mnumCharCodeLen = numCharCodeLen_i

        mnumEscapeSequenceLen = mstrMarkOpen.Length + mnumCharCodeLen + mstrMarkClose.Length

        Dim strMarkCloseE = NSRegExp.Regex.Escape(mstrMarkClose)
        Dim numCharCodeLenMax As Int32

        Select Case menmCharCodeFormat
            Case ENumberFormat.Dec
                mobjREValue = New NSRegExp.Regex("^\d+" + strMarkCloseE, NSRegExp.RegexOptions.Compiled Or NSRegExp.RegexOptions.Singleline Or NSRegExp.RegexOptions.CultureInvariant)
                mblnIsBE = True
                mnumConvertionBase = 10
                numCharCodeLenMax = 10
            Case ENumberFormat.BinBE
                mobjREValue = New NSRegExp.Regex("^[01]+" + strMarkCloseE, NSRegExp.RegexOptions.Compiled Or NSRegExp.RegexOptions.Singleline Or NSRegExp.RegexOptions.CultureInvariant)
                mblnIsBE = True
                mnumConvertionBase = 2
                mnumCharPerByte = 8
                numCharCodeLenMax = mnumCharPerByte * 4
            Case ENumberFormat.BinLE
                mobjREValue = New NSRegExp.Regex("^[01]+" + strMarkCloseE, NSRegExp.RegexOptions.Compiled Or NSRegExp.RegexOptions.Singleline Or NSRegExp.RegexOptions.CultureInvariant)
                mblnIsBE = False
                mnumConvertionBase = 2
                mnumCharPerByte = 8
                numCharCodeLenMax = mnumCharPerByte * 4
            Case ENumberFormat.Oct
                mobjREValue = New NSRegExp.Regex("^[0-7]+" + strMarkCloseE, NSRegExp.RegexOptions.Compiled Or NSRegExp.RegexOptions.Singleline Or NSRegExp.RegexOptions.CultureInvariant)
                mblnIsBE = True
                mnumConvertionBase = 8
                numCharCodeLenMax = 11
            Case ENumberFormat.HexBE
                mobjREValue = New NSRegExp.Regex("^[0-9A-Fa-f]+" + strMarkCloseE, NSRegExp.RegexOptions.Compiled Or NSRegExp.RegexOptions.Singleline Or NSRegExp.RegexOptions.CultureInvariant)
                mblnIsBE = True
                mnumConvertionBase = 16
                mnumCharPerByte = 2
                numCharCodeLenMax = mnumCharPerByte * 4
            Case ENumberFormat.HexLE
                mobjREValue = New NSRegExp.Regex("^[0-9A-Fa-f]+" + strMarkCloseE, NSRegExp.RegexOptions.Compiled Or NSRegExp.RegexOptions.Singleline Or NSRegExp.RegexOptions.CultureInvariant)
                mblnIsBE = False
                mnumConvertionBase = 16
                mnumCharPerByte = 2
                numCharCodeLenMax = mnumCharPerByte * 4
            Case Else
                Throw New ArgumentOutOfRangeException("enmCharCodeFormat_i")
        End Select

        If Not mblnIsBE AndAlso numCharCodeLen_i < mnumCharPerByte Then
            Throw New ArgumentOutOfRangeException("numCharCodeLen_i")
        End If

        If numCharCodeLen_i > numCharCodeLenMax Then
            Throw New ArgumentOutOfRangeException(String.Format("numCharCodeLen_i", "Max value is {0}", numCharCodeLenMax))
        End If
    End Sub

    Public Overrides ReadOnly Property EscapeSequenceLen As Integer
        Get
            Return mnumEscapeSequenceLen
        End Get
    End Property

    Protected Overrides Sub Impl_BegParce(strTextRaw_i As String, ByRef numPosCurr_io As Integer, ByRef numRank_o As Integer)
        mstrCharCodeFounded = Nothing

        If strTextRaw_i.Length >= numPosCurr_io + mstrMarkOpen.Length + mstrMarkClose.Length _
        AndAlso strTextRaw_i.Substring(numPosCurr_io, mstrMarkOpen.Length) = mstrMarkOpen Then
            Dim strTextRawCut As String

            If strTextRaw_i.Length >= numPosCurr_io + mnumEscapeSequenceLen Then
                strTextRawCut = strTextRaw_i.Substring(numPosCurr_io + mstrMarkOpen.Length, mnumEscapeSequenceLen - mstrMarkOpen.Length)
            Else
                strTextRawCut = strTextRaw_i.Substring(numPosCurr_io + mstrMarkOpen.Length)
            End If

            Dim objREM = mobjREValue.Match(strTextRawCut)

            If objREM.Success Then
                mstrCharCodeFounded = objREM.Value.Substring(0, objREM.Length - mstrMarkClose.Length)
                numRank_o = mnumRank
            End If
        End If
    End Sub

    Protected Overrides Sub Impl_EndParce(strTextRaw_i As String, ByRef numPosCurr_io As Integer, ByRef objToken_o As CTokenBase)
        Dim numPosBeg = numPosCurr_io

        numPosCurr_io += mstrMarkOpen.Length

        Dim strTextParced As String = Nothing

        If mblnIsBE Then
            strTextParced = mobjEncoding.GetChars(BitConverter.GetBytes(Convert.ToInt32(mstrCharCodeFounded, mnumConvertionBase)))(0)
        Else
            Dim numBytLen = mnumCharCodeLen \ mnumCharPerByte

            If (mstrCharCodeFounded.Length Mod mnumCharPerByte <> 0) Then
                Throw New XWackyParceError("Invalid escape sequence.", numPosCurr_io)
            End If

            Dim cllByteCode = BitConverter.GetBytes(Convert.ToInt32(mstrCharCodeFounded, mnumConvertionBase))

            Array.Reverse(cllByteCode, 0, (mstrCharCodeFounded.Length \ mnumCharPerByte))
            strTextParced = mobjEncoding.GetChars(cllByteCode, 0, numBytLen)(0)
        End If

        numPosCurr_io += mstrCharCodeFounded.Length + mstrMarkClose.Length
        TokenCreateSafe(strTextRaw_i, numPosBeg, CUInt(mstrMarkOpen.Length + mstrCharCodeFounded.Length + mstrMarkClose.Length), strTextParced, objToken_o)
    End Sub
End Class

Public Class CTokenParcerBorders
    Inherits CTokenParcerComplexBase

    Protected mstrMarkClose As String
    Protected mstrMarkOpen As String
    Protected menmComparisonType As StringComparison
    Protected mblnMoveAboveOpen As Boolean
    Protected mblnMoveAboveClose As Boolean

    Public Property MarkOpen As String
        Get
            Return mstrMarkOpen
        End Get
        Set(str_i As String)
            If String.IsNullOrEmpty(str_i) Then
                mstrMarkOpen = Nothing
            Else
                mstrMarkOpen = str_i
            End If
        End Set
    End Property

    Public Property MarkClose As String
        Get
            Return mstrMarkClose
        End Get
        Set(str_i As String)
            If String.IsNullOrEmpty(str_i) Then
                mstrMarkClose = Nothing
            Else
                mstrMarkClose = str_i
            End If
        End Set
    End Property

    Public Overridable Property ComparisonType As StringComparison
        Get
            Return menmComparisonType
        End Get
        Set(enm_i As StringComparison)
            menmComparisonType = enm_i
        End Set
    End Property

    Public Property MoveAboveOpen As Boolean
        Get
            Return mblnMoveAboveOpen
        End Get
        Set(bln_i As Boolean)
            mblnMoveAboveOpen = bln_i
        End Set
    End Property

    Public Property MoveAboveClose As Boolean
        Get
            Return mblnMoveAboveClose
        End Get
        Set(bln_i As Boolean)
            mblnMoveAboveClose = bln_i
        End Set
    End Property

    Protected Overrides Sub Impl_BegParce(strTextRaw_i As String, ByRef numPosCurr_io As Integer, ByRef numRank_o As Integer)
        If mstrMarkOpen Is Nothing Then
            numRank_o = mnumRank
        ElseIf MStringUtil.StringEqualsSafe(strTextRaw_i, numPosCurr_io, mstrMarkOpen, menmComparisonType) Then
            If mblnMoveAboveOpen Then numPosCurr_io += mstrMarkOpen.Length

            numRank_o = mnumRank
        End If
    End Sub

    Protected Overrides Sub Impl_EndParce(strTextRaw_i As String, ByRef numPosCurr_io As Integer, ByRef objToken_o As CTokenBase)
        Dim numPosBeg = numPosCurr_io

        If mstrMarkClose Is Nothing Then
            TokenCreateSafe(strTextRaw_i, numPosBeg, 0, Nothing, objToken_o)
        ElseIf MStringUtil.StringEqualsSafe(strTextRaw_i, numPosCurr_io, mstrMarkClose, menmComparisonType) Then
            If mblnMoveAboveClose Then numPosCurr_io += mstrMarkClose.Length

            TokenCreateSafe(strTextRaw_i, numPosBeg, CUInt(mstrMarkClose.Length), Nothing, objToken_o)
        Else
            Throw New XWackyParceError("Unexpected char.", numPosCurr_io)
        End If
    End Sub


    Sub New(objSeed As CTokenParcerBorders)
        MyBase.New(objSeed)
        With objSeed
            mstrMarkClose = .mstrMarkClose
            mstrMarkOpen = .mstrMarkOpen
            menmComparisonType = .menmComparisonType
            mblnMoveAboveOpen = .mblnMoveAboveOpen
            mblnMoveAboveClose = .mblnMoveAboveClose
        End With
    End Sub


    Public Sub New(numRank_i As Int32, objTokenFactory_i As ITokenFactory)
        MyClass.New(numRank_i, objTokenFactory_i, Nothing, Nothing, StringComparison.Ordinal)
    End Sub

    Public Sub New(numRank_i As Int32, objTokenFactory_i As ITokenFactory, ByVal strMarkOpen_i As String, ByVal strMarkClose_i As String)
        MyClass.New(numRank_i, objTokenFactory_i, strMarkOpen_i, strMarkClose_i, StringComparison.Ordinal)
    End Sub


    Public Sub New(numRank_i As Int32, objTokenFactory_i As ITokenFactory, ByVal strMarkOpen_i As String, ByVal strMarkClose_i As String, ByVal enmComparisonType_i As StringComparison)
        MyBase.New(numRank_i, objTokenFactory_i)

        If String.IsNullOrEmpty(strMarkOpen_i) Then
            mstrMarkOpen = Nothing
        Else
            mstrMarkOpen = strMarkOpen_i
        End If

        If String.IsNullOrEmpty(strMarkClose_i) Then
            mstrMarkClose = Nothing
        Else
            mstrMarkClose = strMarkClose_i
        End If

        menmComparisonType = enmComparisonType_i
        mblnMoveAboveOpen = True
        mblnMoveAboveClose = True
    End Sub

    Public Sub New(numRank_i As Int32)
        MyClass.New(numRank_i, Nothing, Nothing, StringComparison.Ordinal)
    End Sub

    Public Sub New(numRank_i As Int32, ByVal strMarkOpen_i As String, ByVal strMarkClose_i As String)
        MyClass.New(numRank_i, strMarkOpen_i, strMarkClose_i, StringComparison.Ordinal)
    End Sub

    Public Sub New(numRank_i As Int32, ByVal strMarkOpen_i As String, ByVal strMarkClose_i As String, ByVal enmComparisonType_i As StringComparison)
        MyBase.New(numRank_i)

        If strMarkOpen_i.Length = 0 Then
            mstrMarkOpen = Nothing
        Else
            mstrMarkOpen = strMarkOpen_i
        End If

        If strMarkClose_i.Length = 0 Then
            mstrMarkClose = Nothing
        Else
            mstrMarkClose = strMarkClose_i
        End If

        menmComparisonType = enmComparisonType_i
        mblnMoveAboveOpen = True
        mblnMoveAboveClose = True
    End Sub

End Class
